﻿using System;
using CI.Interfaces;
using CI.Misc;
using CI.Organism;
using NUnit.Framework;
// ReSharper disable InconsistentNaming

namespace Mesopotamia.UnitTests.Neural
{
    [TestFixture]
    public class VisionBumperLearningTests
    {
        SimpleNeuralSystem _sns;
        CI.Organism.Organism _organism;
        private readonly IHostingEnvironment _hostingEnvironment = new CI.Mesopotamia.MSRBHost(new MockHostingEnvironmentPhysical(), new CI.Mesopotamia.ScenarioLocalXML());
        private const int inboundFiringValue = 101;


        [Test]
        public void Evaluate_TestLearnedBehaviour_True()
        {
            _hostingEnvironment.Organisms = new OrganismCollection();
            _organism = new CI.Organism.Organism(_hostingEnvironment);
            _sns = new SimpleNeuralSystem(_organism);
            _organism.NeuralSystem = _sns;

            bool fired = false;

            // create nn
            var bumper = new SynapseIO("bumper", string.Empty, string.Empty, Guid.NewGuid());
            var brightness = new SynapseIO("brightness", string.Empty, string.Empty, Guid.NewGuid());
            var outputTurn = new SynapseIO("turn", string.Empty, string.Empty, Guid.NewGuid());

            var neuron = new StepNeuron(10, _sns, Guid.NewGuid());

            var bumperToNeuronSynapse = new SimpleSynapse(Guid.NewGuid(), _sns) { InitInputWeight = 1 };
            bumper.ActivationEvent += bumperToNeuronSynapse.FireInput;
            bumperToNeuronSynapse.AddSynapticConnectionSource(bumper);
            bumperToNeuronSynapse.AddSynapticConnectionTarget(neuron);
            neuron.InputSynapse.Add(bumperToNeuronSynapse);
            var bumperToNeuronLearningRule = new UseItOrLoseIt(1f, 0.01f, bumperToNeuronSynapse, Guid.NewGuid());

            var brightnessToNeuronSynapse = new SimpleSynapse(Guid.NewGuid(), _sns) { InitInputWeight = 1 };
            brightness.ActivationEvent += brightnessToNeuronSynapse.FireInput;
            brightnessToNeuronSynapse.AddSynapticConnectionSource(brightness);
            brightnessToNeuronSynapse.AddSynapticConnectionTarget(neuron);
            neuron.InputSynapse.Add(brightnessToNeuronSynapse);
            var brightnessToNeuronLearningRule = new UseItOrLoseIt(11f, 0.01f, brightnessToNeuronSynapse, Guid.NewGuid());

            var neuronToOuputSynapse = new SimpleSynapse(Guid.NewGuid(), _sns) { InitInputWeight = 1 };
            neuronToOuputSynapse.AddSynapticConnectionSource(neuron);
            neuronToOuputSynapse.AddSynapticConnectionTarget(outputTurn);
            neuron.OutputSynapse.Add(neuronToOuputSynapse);
            var neuronToOutputLearningRule = new UseItOrLoseIt(1f, 0.01f, neuronToOuputSynapse, Guid.NewGuid());

            outputTurn.ActivationEvent += (o) => fired = true;

            _sns.AddNeuron(neuron);


            // test against parameters
            var counter = 0;
            var totalCounter = 0;
            const int firingCounter = 100;
            var blankTestFire = false;
            do
            {
                counter++;
                totalCounter++;
                var brightnessValue = 0;
                switch (counter)
                {
                    case 1:
                        brightnessValue = 90;
                        break;
                    case 2:
                        brightnessValue = 80;
                        break;
                    case firingCounter / 2:
                        brightnessValue = 50;
                        break;
                    case firingCounter - 2:
                        brightnessValue = 80;
                        break;
                    case firingCounter - 1:
                        brightnessValue = 90;
                        break;
                    case firingCounter:
                        brightnessValue = 100;
                        break;
                }

                // test fire bumper
                fired = false;
                if (counter == firingCounter)
                    bumper.FireInput(inboundFiringValue);

                brightnessToNeuronSynapse.FireInput(brightnessValue);
                _sns.Evaluate();

                if (counter == firingCounter)
                {
                    Assert.IsTrue(fired);
                    counter = 0;
                }

                if (counter == (firingCounter / 2) && fired)
                    blankTestFire = true;

            } while (!blankTestFire && totalCounter < 51);

            Assert.IsTrue(blankTestFire);

        }
    }
}
